package co.bugu.code.analysis.service;

import co.bugu.code.analysis.service.entity.CodeFileInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.sql.DataSource;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 分析代码
 *
 * 计算代码行数，注释行数， 方法块数量， 注释块数量
 *
 * @author daocers
 * @date 2025/2/27 16:04
 */
@Service
@Slf4j
public class AnalysisService {

    @Autowired
    private DataSource dataSource;

    /**
     * 分析数据库中各列的NULL比例
     *
     * @param tableName 表名
     * @return 包含各列NULL比例的结果映射
     */
    public Map<String, Double> analyzeNullRatio(String tableName) {
        Map<String, Double> nullRatioMap = new HashMap<>();
        try (Connection connection = dataSource.getConnection()) {
            // 获取表的元数据
            DatabaseMetaData metaData = connection.getMetaData();
            ResultSet columnsResultSet = metaData.getColumns(null, null, tableName, null);

            // 遍历所有列
            while (columnsResultSet.next()) {
                String columnName = columnsResultSet.getString("COLUMN_NAME");
                double nullRatio = calculateNullRatio(connection, tableName, columnName);
                nullRatioMap.put(columnName, nullRatio);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return nullRatioMap;
    }

    /**
     * 分析数据库中所有表的各列NULL比例
     *
     * @return 包含每个表各列NULL比例的结果映射
     */
    public Map<String, Map<String, Double>> analyzeAllTables() {
        Map<String, Map<String, Double>> allTablesNullRatios = new HashMap<>();
        try (Connection connection = dataSource.getConnection()) {
            DatabaseMetaData metaData = connection.getMetaData();
            ResultSet tablesResultSet = metaData.getTables(null, null, "%", new String[]{"TABLE"});

            while (tablesResultSet.next()) {
                String tableName = tablesResultSet.getString("TABLE_NAME");
                Map<String, Double> tableNullRatios = new HashMap<>();

                // 获取表的元数据
                ResultSet columnsResultSet = metaData.getColumns(null, null, tableName, null);

                // 遍历所有列
                while (columnsResultSet.next()) {
                    String columnName = columnsResultSet.getString("COLUMN_NAME");
                    double nullRatio = calculateNullRatio(connection, tableName, columnName);
                    tableNullRatios.put(columnName, nullRatio);
                }

                allTablesNullRatios.put(tableName, tableNullRatios);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return allTablesNullRatios;
    }

    /**
     * 找出NULL比例最高的列
     *
     * @param allTablesNullRatios 包含每个表各列NULL比例的结果映射
     * @return 返回包含表名、列名和NULL比例的列表
     */
    public List<ColumnInfo> findHighestNullRatioColumns(Map<String, Map<String, Double>> allTablesNullRatios) {
        List<ColumnInfo> columnInfoList = new ArrayList<>();

        for (Map.Entry<String, Map<String, Double>> tableEntry : allTablesNullRatios.entrySet()) {
            String tableName = tableEntry.getKey();
            Map<String, Double> columnRatios = tableEntry.getValue();

            for (Map.Entry<String, Double> columnEntry : columnRatios.entrySet()) {
                String columnName = columnEntry.getKey();
                double nullRatio = columnEntry.getValue();

                columnInfoList.add(new ColumnInfo(tableName, columnName, nullRatio));
            }
        }

        // 按照NULL比例降序排序
        columnInfoList.sort((o1, o2) -> Double.compare(o2.getNullRatio(), o1.getNullRatio()));

        return columnInfoList;
    }

    /**
     * 计算指定列的NULL比例
     *
     * @param connection 数据库连接
     * @param tableName  表名
     * @param columnName 列名
     * @return NULL比例
     */
    private double calculateNullRatio(Connection connection, String tableName, String columnName) throws SQLException {
        String countQuery = "SELECT COUNT(*) FROM " + tableName;
        String nullCountQuery = "SELECT COUNT(*) FROM " + tableName + " WHERE " + columnName + " IS NULL";

        try (Statement statement = connection.createStatement();
             ResultSet countResultSet = statement.executeQuery(countQuery);
             ResultSet nullCountResultSet = statement.executeQuery(nullCountQuery)) {

            int totalCount = 0;
            if (countResultSet.next()) {
                totalCount = countResultSet.getInt(1);
            }

            int nullCount = 0;
            if (nullCountResultSet.next()) {
                nullCount = nullCountResultSet.getInt(1);
            }

            if (totalCount == 0) {
                return 0.0;
            }

            return (double) nullCount / totalCount;
        }
    }

    public static void main(String[] args) {
        String filePath = "path/to/your/JavaFile.java"; // 替换为你的Java文件路径
        try {
            double ratio = calculateCommentRatio(new File(filePath));
            System.out.printf("Public methods with block comments ratio: %.2f%%\n", ratio * 100);
        } catch (IOException e) {
            System.err.println("Error reading file: " + e.getMessage());
        }
    }

    /**
     * 计算Java文件中public方法的块级注释比例
     *
     * @param file Java文件路径
     * @return 块级注释比例（0到1之间）
     * @throws IOException 如果文件读取失败
     */
    public static double calculateCommentRatio(File file) throws IOException {
        int publicMethodCount = 0; // public方法总数
        int commentedMethodCount = 0; // 带有块级注释的public方法数量

        try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
            String line;
            StringBuilder methodBuffer = new StringBuilder();
            boolean isPublicMethod = false;
            boolean hasBlockComment = false;

            while ((line = reader.readLine()) != null) {
                line = line.trim();

                // 检测public方法
                if (line.startsWith("public") && line.contains("(") && line.endsWith("{")) {
                    isPublicMethod = true;
                    publicMethodCount++;
                    methodBuffer.setLength(0); // 清空缓冲区
                    methodBuffer.append(line).append("\n");
                }

                // 检测块级注释
                if (isPublicMethod && line.startsWith("/**")) {
                    hasBlockComment = true;
                }

                // 方法结束（以"}"结尾）
                if (isPublicMethod && line.endsWith("}")) {
                    if (hasBlockComment) {
                        commentedMethodCount++;
                    }
                    isPublicMethod = false;
                    hasBlockComment = false;
                }

                // 将方法内容存入缓冲区（可选，用于调试）
                if (isPublicMethod) {
                    methodBuffer.append(line).append("\n");
                }
            }
        }

        if (publicMethodCount == 0) {
            return 0.0; // 如果没有public方法，返回0
        }

        return (double) commentedMethodCount / publicMethodCount;
    }


    /**
     * 计算指定工程的的注释率
     *
     * @param projectDir git 项目所在的目录
     * @return
     * @author alei
     * @date 2025/2/27 17:40
     */
    public List<CodeFileInfo> analyseProject(File projectDir) throws Exception {
        List<CodeFileInfo> res = new ArrayList<>();
        if (projectDir.isFile()) {
            projectDir.setReadable(true);
            String name = projectDir.getName();
            if (name.endsWith(".java")) {
                CodeFileInfo codeRes = processFile(projectDir);
                res.add(codeRes);
            }
        } else {
            if (!projectDir.canRead()) {
                throw new RuntimeException(String.format("%s 没有读权限", projectDir.getPath()));
            }
            File[] files = projectDir.listFiles();
            for (File file : files) {
//                递归调用
                List<CodeFileInfo> codeRes = analyseProject(file);
                if (CollectionUtils.isNotEmpty(codeRes)) {
                    res.addAll(codeRes);
                }
            }
        }
        if (CollectionUtils.isNotEmpty(res)) {
            res.forEach(item -> item.setProject(projectDir.getPath()));
        }
        return res;

    }

    public String getContent(File file) throws IOException {
        BufferedReader reader = new BufferedReader(new FileReader(file));

        StringBuffer buffer = new StringBuffer();
        String line;

        while ((line = reader.readLine()) != null) {
            buffer.append(line).append("\n");
        }
        return buffer.toString();
    }


    /**
     * 处理文件，获取文件信息
     *
     * @param
     * @return
     * @author alei
     * @date 2025/3/17 14:47
     */
    public CodeFileInfo processFile(File file) throws Exception {
        int commentCount = 0;
        int methodCount = 0;
        int lineCount = 0;
        int commentLineCount = 0;

        boolean isInsideBlockComment = false; // 是否在块级注释中

//  注释 reg
        String commentRegex = "/\\*\\*.*?\\*/"; // 匹配块级注释
        Pattern commentPattern = Pattern.compile(commentRegex, Pattern.DOTALL);

//        方法reg
        String methodRegex = "\\bpublic\\s+(?:\\w+\\S*\\s+)?\\w+\\s*\\([^)]*\\).*\\{"; // 匹配public方法
        Pattern methodPattern = Pattern.compile(methodRegex);


        StringBuilder fileContent = new StringBuilder();
        BufferedReader reader = new BufferedReader(new FileReader(file));
        String line;
        while ((line = reader.readLine()) != null) {
            fileContent.append(line).append("\n");

            line = line.trim(); // 去除行首和行尾的空格

            if (StringUtils.isBlank(line)) {
                continue;
            }
//            判断方法
            Matcher matcher = methodPattern.matcher(line);
            if (matcher.find()) {
                methodCount++;
            }

            lineCount++;

            // 检查是否进入块级注释
            if (line.startsWith("/*")) {
                isInsideBlockComment = true;
            }

            // 如果在块级注释中，增加注释行数
            if (isInsideBlockComment) {
                commentLineCount++;
            }

            // 检查是否退出块级注释
            if (line.endsWith("*/")) {
                isInsideBlockComment = false;
            }
        }

        Matcher matcher = commentPattern.matcher(fileContent.toString());
        while (matcher.find()) {
            commentCount++;
        }

        CodeFileInfo res = new CodeFileInfo();
        res.setFile(file.getPath().replace("D:\\worker\\yingyun\\", ""));
        res.setType(detectType(fileContent.toString()));
        res.setProject(null);
        res.setCommentLineCount(commentLineCount);
        res.setLineCount(lineCount);
        res.setBlockCommentCount(commentCount);
        res.setMethodCount(methodCount);

        double lineRatio = 0.00;
        if (lineCount != 0) {
            lineRatio = Math.round(commentLineCount * 100 / lineCount);
        }

        double blockRatio = 0.00;
        if (methodCount != 0) {
            blockRatio = Math.round(commentCount * 100 / methodCount);
        }
        res.setCommentLineRate(lineRatio);
//        res.setCommentMethodRatio(blockRatio);
        return res;
    }


    /**
     * 检测文件类型
     *
     * @param
     * @return
     * @author alei
     * @date 2025/3/17 14:46
     */
    public static String detectType(String sourceCode) {
        // 预处理：移除注释和字符串内容（简化示例，实际需更严谨处理）
        String cleaned = sourceCode
                .replaceAll("/\\*.*?\\*/", "")  // 移除块注释
                .replaceAll("//.*", "")         // 移除行注释
                .replaceAll("\".*?\"", "");     // 移除字符串内容

        // 匹配类型声明模式
        Pattern pattern = Pattern.compile(
                "^\\s*(?:public|final|abstract)?\\s*(class|interface|enum)\\b",
                Pattern.MULTILINE | Pattern.DOTALL
        );
        Matcher matcher = pattern.matcher(cleaned);

        if (matcher.find()) {
            String res = matcher.group(1);
            return res;
        }
        return "未知";
    }


    /**
     * 计算指定目录下的项目的注释信息
     *
     * @param codeDir 代码文件夹
     * @return
     * @author alei
     * @date 2025/3/18 13:22
     */
    public List<CodeFileInfo> calculateCommentInfoOfDir(File codeDir) throws Exception {
        List<CodeFileInfo> res = new ArrayList<>();
        if (!codeDir.exists() || codeDir.isFile()) {
            return res;
        }
        File[] files = codeDir.listFiles();
        List<File> collect = Arrays.stream(files).filter(item -> item.getName().equals(".git")).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(collect)) {
            List<CodeFileInfo> codeRes = analyseProject(codeDir);
            if (CollectionUtils.isNotEmpty(codeRes)) {
                res.addAll(codeRes);
            }

        } else {
            for (File item : files) {
                List<CodeFileInfo> codeFileInfo = calculateCommentInfoOfDir(item);
                if (CollectionUtils.isNotEmpty(codeFileInfo)) {
                    res.addAll(codeFileInfo);
                }
            }
        }
        return res;
    }


    // 用于存储表名、列名和NULL比例的简单类
    public static class ColumnInfo {
        private String tableName;
        private String columnName;
        private double nullRatio;

        public ColumnInfo(String tableName, String columnName, double nullRatio) {
            this.tableName = tableName;
            this.columnName = columnName;
            this.nullRatio = nullRatio;
        }

        // Getters and setters
        public String getTableName() {
            return tableName;
        }

        public void setTableName(String tableName) {
            this.tableName = tableName;
        }

        public String getColumnName() {
            return columnName;
        }

        public void setColumnName(String columnName) {
            this.columnName = columnName;
        }

        public double getNullRatio() {
            return nullRatio;
        }

        public void setNullRatio(double nullRatio) {
            this.nullRatio = nullRatio;
        }
    }
}